<?php
/**
 * Copyright (c) [2019] [吴跃忠]
 * [selibra] is licensed under the Mulan PSL v1.
 * You can use this software according to the terms and conditions of the Mulan PSL v1.
 * You may obtain a copy of Mulan PSL v1 at:
 * http://license.coscl.org.cn/MulanPSL
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
 * See the Mulan PSL v1 for more details.
 */

namespace Selibra\Di\Container;


use Selibra\Di\AnnotationRegister as SelibraAnnotationRegister;
use Selibra\Di\ContainerObjectInitialization;
use Selibra\Di\DI;
use Selibra\Di\Events\ContainerInitializedEvent;
use Selibra\Event\EventTrigger;
use Selibra\Files\Config;
use Selibra\Files\Ignore;
use Selibra\Files\Manager;
use Selibra\Files\Rule;
use Selibra\Tools\Tools;
use Selibra\Tools\Console;

class ContainerBootstrap
{


    /**
     * @throws \Exception
     */
    public static function init()
    {
        $fileRule = new Rule();
        $fileRule->setSuffix('.php');
        $fileRule->setPath(Config::getRootPath());
        $fileRule->setIgnore(self::getDiIgnore());
        $fileManager = new Manager();
        Console::logNF("Program file reading");
        $files = $fileManager->read($fileRule)->files(true);
        Console::logNF("A total of " . count($files) . " available PHP file");

        // 将对象装载进容器
        $container = new Container();
        $metadataArray = [];
        Console::logNF("Object metadata initialization");
        try {
            foreach ($files as $file) {
                // 引入文件
                require_once $file;

                // 获取命名空间
                $realNamespace = Tools::getRealNamespaceByFilepath($file);
                if (!empty($realNamespace)) {
                    $namespace = Tools::getDecRootNamespace($realNamespace);
                    if (!class_exists($namespace, false)) {
                        continue;
                    }
                    // 获取类注解
                    $metadata = SelibraAnnotationRegister::getMetadata($namespace);
                    if (null !== $metadata) {
                        $metadataArray[$namespace] = $metadata;
                        $container->collector()->add($namespace, $metadata);
                    }
                }
            }
        } catch (\Throwable $exception) {
            Console::log($exception);
        }

        // 设置Container为静态变量
        DI::setContainer($container);

        Console::logNF("Attributes initialization");
        // 初始化
        foreach ($metadataArray as $namespace => $metadata) {
            $containerObjectInitialization = new ContainerObjectInitialization($metadata);
            $containerObjectInitialization->run();
        }

        Console::logNF("Component Attribute object initialization");
        foreach ($metadataArray as $namespace => $metadata) {
            // 获取类注解
            $containerObjectInitialization = new ContainerObjectInitialization($metadata);
            $containerObjectInitialization->runComponentAnnotation();
        }
        // 容器初始化成功事件
        EventTrigger::listener((new ContainerInitializedEvent()));
        unset($metadataArray);
        return $container;
    }


    /**
     * @return Ignore
     */
    private static function getDiIgnore(): Ignore
    {
        $vendorAllow = \Selibra\Config\Config::get('application.di.vendor-allow');
        $vendorAllow['selibra'] = '1';
        $vendorAllowDirs = [];
        foreach ($vendorAllow as $ignore => $state) {
            $ignore = str_replace('.', '/', $ignore);
            $ignoresFile = Config::getRootPath() . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . $ignore;
            $vendorAllowDirs[$ignoresFile] = $state;
        }
        $diIgnore = new Ignore();
        $fileRule = new Rule();
        $fileRule->setPath(Config::getRootPath() . DIRECTORY_SEPARATOR . 'vendor');
        $fileManager = new Manager();
        $dirs = $fileManager->read($fileRule)->dirs();
        foreach ($dirs as $item) {
            if (empty($vendorAllowDirs[$item])) {
                $diIgnore->add($item);
            }
        }
        return $diIgnore;
    }
}
